/*
 * SwingUtilitiesDelegate.cpp  0.4.0 / Nov 17, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "SwingUtilitiesDelegate.h"

#include "Cute4jSwing.h"

// SwingUtilitiesDelegate :: Private Constructors. -----------------------------

SwingUtilitiesDelegate::SwingUtilitiesDelegate()
  : QObject(0)
{
  connect(this, SIGNAL(invokeLaterSignal(jobject)), this,
          SLOT(invokeLaterSlot(jobject)), Qt::QueuedConnection);
  connect(this, SIGNAL(invokeAndWaitSignal(TargetFrame *)), this,
          SLOT(invokeAndWaitSlot(TargetFrame *)), Qt::BlockingQueuedConnection);
  connect(this, SIGNAL(invokeSafeAndWaitSignal(jobject)), this,
          SLOT(invokeLaterSlot(jobject)), Qt::BlockingQueuedConnection);
  connect(this, SIGNAL(invokeIntAndWaitSignal(IntMethodFrame *)), this,
          SLOT(invokeIntAndWaitSlot(IntMethodFrame *)),
          Qt::BlockingQueuedConnection);
  connect(this, SIGNAL(invokeLongAndWaitSignal(LongMethodFrame *)), this,
          SLOT(invokeLongAndWaitSlot(LongMethodFrame *)),
          Qt::BlockingQueuedConnection);
}

SwingUtilitiesDelegate::SwingUtilitiesDelegate(const SwingUtilitiesDelegate &)
  : QObject(0)
{
  // disabled
}

// SwingUtilitiesDelegate :: Private Destructor. -------------------------------

SwingUtilitiesDelegate::~SwingUtilitiesDelegate()
{
  // disabled
}

// SwingUtilitiesDelegate :: Static Native Calls. ------------------------------

/*!
 * \brief Invokes runnable target from the GUI loop.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Java SwingUtilities class
 * \param target the runnable target object
 */
void SwingUtilitiesDelegate::invokeLater(JNIEnv *env, jclass cls,
                                         jobject target)
{
  Q_UNUSED(cls);
  swingUtilitiesDelegate->invokeLater(JNIWrapper::newGlobalRef(env, target));
}

/*!
 * \brief Invokes runnable target from the  GUI loop and waits until finish.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Java SwingUtilities class
 * \param target the runnable target object
 */
void SwingUtilitiesDelegate::invokeAndWait(JNIEnv *env, jclass cls,
                                           jobject target)
{
  Q_UNUSED(cls);
  TargetFrame targetFrame;
  targetFrame.target = JNIWrapper::newGlobalRef(env, target);
  swingUtilitiesDelegate->invokeAndWait(&targetFrame);
  if (targetFrame.exception) {
    jclass cls = env->FindClass("java/lang/reflect/InvocationTargetException");
    if (cls != 0) {
      jthrowable ite = (jthrowable)env->NewObject(cls, sm_iteConstructorMID,
                                                  targetFrame.exception);
      if (ite) {
        env->Throw(ite);
        env->DeleteLocalRef(ite);
      } else {
        JNIWrapper::checkException(env);
      }
      env->DeleteLocalRef(cls);
    } else {
      JNIWrapper::checkException(env);
    }
    env->DeleteGlobalRef(targetFrame.exception);
  }
  env->DeleteGlobalRef(targetFrame.target);
}

/*!
 * \brief Invokes "safe" runnable target from the GUI loop and waits until
 *        finish.
 *
 * \param env the JNIEnv pointer
 * \param cls the Java SwingUtilities class
 * \param safeTarget the safe runnable target object
 */
void SwingUtilitiesDelegate::invokeSafeAndWait(JNIEnv *env, jclass cls,
                                               jobject safeTarget)
{
  Q_UNUSED(cls);
  swingUtilitiesDelegate->invokeSafeAndWait(
                                    JNIWrapper::newGlobalRef(env, safeTarget));
}

// SwingUtilitiesDelegate :: Static Member Functions. --------------------------

/*!
 * \brief Initializes SwingUtilitiesDelegate class.
 *
 * \param env the JNIEnv pointer 
 * \param runnableClass the Java Runnable class
 * \param iteClass the Java InvocationTargetException class
 */
void SwingUtilitiesDelegate::initialize(JNIEnv *env, jclass runnableClass,
                                        jclass iteClass)
{
  Q_ASSERT(env);
  Q_ASSERT(runnableClass);
  Q_ASSERT(iteClass);
  Q_ASSERT(!sm_runMID);
  if (!INIT_MID(run, runnableClass, "()V") ||
      !INIT_CONSTRUCTOR(iteConstructor, iteClass, "(Ljava/lang/Throwable;)V")) {
    FATAL_LOGGED_ERROR("SwingUtilitiesDelegate initialization failed");
  }
  swingUtilitiesDelegate = new SwingUtilitiesDelegate();
}

/*!
 * \brief Invokes int method of the specifed object from the Swing thread and
 *        waits until finish.
 *
 * \param env the JNIEnv pointer
 * \param obj the Java object
 * \param mid the method ID
 * \param ... the method arguments
 *
 * \return method result
 */
jint SwingUtilitiesDelegate::invokeIntAndWait(JNIEnv *env, jobject obj,
                                              jmethodID mid, ...)
{
  Q_ASSERT(mid);
  IntMethodFrame methodFrame;
  methodFrame.obj = JNIWrapper::newGlobalRef(env, obj);
  methodFrame.mid = mid;
  va_start(methodFrame.args, mid);
  swingUtilitiesDelegate->invokeIntAndWait(&methodFrame);
  va_end(methodFrame.args);
  env->DeleteGlobalRef(methodFrame.obj);
  return methodFrame.result;
}

/*!
 * \brief Invokes long method of the specifed object from the Swing thread and
 *        waits until finish.
 *
 * \param env the JNIEnv pointer
 * \param obj the Java object
 * \param mid the method ID
 * \param ... the method arguments
 *
 * \return method result
 */
jlong SwingUtilitiesDelegate::invokeLongAndWait(JNIEnv *env, jobject obj,
                                                jmethodID mid, ...)
{
  Q_ASSERT(mid);
  LongMethodFrame methodFrame;
  methodFrame.obj = JNIWrapper::newGlobalRef(env, obj);
  methodFrame.mid = mid;
  va_start(methodFrame.args, mid);
  swingUtilitiesDelegate->invokeLongAndWait(&methodFrame);
  va_end(methodFrame.args);
  env->DeleteGlobalRef(methodFrame.obj);
  return methodFrame.result;
}

// SwingUtilitiesDelegate :: Private Member Functions. -------------------------

void SwingUtilitiesDelegate::invokeLater(jobject target)
{
  emit invokeLaterSignal(target);
}

void SwingUtilitiesDelegate::invokeAndWait(TargetFrame *targetFrame)
{
  emit invokeAndWaitSignal(targetFrame);
}

void SwingUtilitiesDelegate::invokeSafeAndWait(jobject safeTarget)
{
  emit invokeSafeAndWaitSignal(safeTarget);
}

void SwingUtilitiesDelegate::invokeIntAndWait(IntMethodFrame *methodFrame)
{
  emit invokeIntAndWaitSignal(methodFrame);
}

void SwingUtilitiesDelegate::invokeLongAndWait(LongMethodFrame *methodFrame)
{
  emit invokeLongAndWaitSignal(methodFrame);
}

void SwingUtilitiesDelegate::invokeLaterSlot(jobject target)
{
  JNISwingWrapper::callVoidMethod(target, sm_runMID);
  JNISwingWrapper::deleteGlobalRef(target);
}

void SwingUtilitiesDelegate::invokeAndWaitSlot(TargetFrame *targetFrame)
{
  SWING_ENV->CallVoidMethod(targetFrame->target, sm_runMID);
  jthrowable exception = SWING_ENV->ExceptionOccurred();
  if (exception) {
    targetFrame->exception = (jthrowable)SWING_ENV->NewGlobalRef(exception);
    SWING_ENV->ExceptionClear();
    SWING_ENV->DeleteLocalRef(exception);
  } else {
    targetFrame->exception = 0;
  }
}

void SwingUtilitiesDelegate::invokeIntAndWaitSlot(IntMethodFrame *methodFrame)
{
  methodFrame->result = SWING_ENV->CallIntMethodV(methodFrame->obj,
                                                  methodFrame->mid,
                                                  methodFrame->args);
  if (JNISwingWrapper::testException()) {
    methodFrame->result = 0;
  }
}

void SwingUtilitiesDelegate::invokeLongAndWaitSlot(LongMethodFrame *methodFrame)
{
  methodFrame->result = SWING_ENV->CallLongMethodV(methodFrame->obj,
                                                   methodFrame->mid,
                                                   methodFrame->args);
  if (JNISwingWrapper::testException()) {
    methodFrame->result = 0;
  }
}

// SwingUtilitiesDelegate :: Private Static Data Members. ----------------------

jmethodID SwingUtilitiesDelegate::sm_runMID = 0;
jmethodID SwingUtilitiesDelegate::sm_iteConstructorMID = 0;
SwingUtilitiesDelegate *SwingUtilitiesDelegate::swingUtilitiesDelegate = 0;
